package nrpc.client.reqst;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import message.io.SingleRequest;
import message.reflect.HandlerMapper;
import nrpc.client.serst.ServiceNode;
import nrpc.client.skst.SocketStrategy;
import nrpc.client.slst.SelectStrategy;
import nrpc.exception.NotServiceNodeException;
import nrpc.message.DefaultHandlerMapper;
import nrpc.message.InvokeMessageParserSupplier;
import nrpc.message.ReturnMessageBuilder;
import nrpc.message.meta.InvokeMessage;

public class SingleRequestStrategy implements RequestStrategy {

	private final static long TIMEOUT = 10000;
	
	private HandlerMapper mapper;
	private SelectStrategy selectStrategy;
	private SocketStrategy socketStrategy;
	
	public SingleRequestStrategy(SelectStrategy selectStrategy, SocketStrategy socketStrategy) throws IOException {
		this.mapper = DefaultHandlerMapper.INSTANCE;
		this.selectStrategy = selectStrategy;
		this.socketStrategy = socketStrategy;
	}
	
	@Override
	public void invoke(Method method, List<Object> args, Type retType, Consumer<Object> callback) {
		InvokeMessage message = new InvokeMessage(0, method.getName(), args);
//		SocketChannel channel = selectStrategy.activateChannel(method, args);
		synchronized (selectStrategy) {
			Iterator<ServiceNode> iterator = null;
			while(true){
				iterator = selectStrategy.getNode(method, args);
				if(iterator.hasNext())break;
				try {
					selectStrategy.wait(TIMEOUT);
				} catch (InterruptedException e) {}
			} 
			while(iterator.hasNext()){
				ServiceNode node = iterator.next();
				try {
					SocketChannel channel = this.socketStrategy.getChannel(node);
					SingleRequest.send(channel, ByteBuffer.allocate(1024), 
							new InvokeMessageParserSupplier(mapper, method).parse(message));
					SingleRequest.recv(channel, ByteBuffer.allocate(1024), 
							new ReturnMessageBuilder(callback, mapper, retType));
					this.socketStrategy.backChannel(node, channel);
					return;
				}
				catch (IOException e) {
					iterator.remove();
					this.socketStrategy.disconnect(node);
				}
			}
			throw new NotServiceNodeException();
		}
				
//		selectStrategy.activateChannel(method, args, channel->{
//			ByteBuffer in = ByteBuffer.allocate(1024), out = ByteBuffer.allocate(1024);
//			try {
//				SingleRequest.send(channel, out, 
//						new InvokeMessageParserSupplier(mapper, method).parse(message));
//				SingleRequest.recv(channel, in, 
//						new ReturnMessageBuilder(callback, mapper, retType));
//			}
//			catch (IOException e) {
//				// 连接断开
//				return false;
//			}	
//			return true;
//		});
	}
	
	@Override
	public void close() {
		selectStrategy.close();
	}
	
}
